查看原文
其他

一个让我惊掉下巴的时间格式化问题!

程序猿DD 2022-03-17

Editor's Note

一个有意思的问题

The following article is from why技术 Author 歪歪

前沿技术早知道,弯道超车有希望
积累超车资本,从关注DD开始

昨天,DD看到一篇挺有意思的文章,相关知识很冷门,甚至可能你看完了觉得没啥卵用。但作者探究真相的过程非常直接大家学习,下面一起来看看吧!


先给你搞个程序看一下:

public class MainTest {

    public static void main(String[] args) throws Exception {
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Date date = simpleDateFormat.parse("1900-01-01 08:00:00");
        System.out.println(simpleDateFormat.format(date));
    }
}

你说上面的程序逻辑就是一个简单的时间格式化,你说输出结果是什么?

只是需要瞟一眼就知道,肯定是输出这个结果呀:

1900-01-01 08:00:00

但是,你把上面的程序拿出来,直接跑起来,你会发现输出结果竟然是这样的:

1900-01-01 08:05:43

当时就懵逼了。

我知道时差 8 小时,是因为有时区问题。

我知道时间差 1 小时,是因为有夏令时的原因。

但是这里差了 5 分 43 秒,有零有整,就让我有点摸不着头脑了。

上面这个案例就是一个读者分享给我的,他们在数据库里面默认时间是 1900-01-01,再加上时区问题,刚好变成了 1900-01-01 08:00:00,于是在通过程序做数据迁移的时候就踩到了这个莫名其妙的时间问题。

同时他还给我附送了一个关于这个 bug 的链接:

https://bugs.openjdk.java.net/browse/JDK-8266262

我乍一看,这个 bug 还挺新的呢,属于今年提出来的。

仔细又看了一眼发现是和之前的 bug 重复了:

但是这里提到了原因:

他说可以看一下这个链接https://www.timeanddate.com/time/zone/china/shanghai?year=1900

这里面,在 1900 年的时候,发生了一个变化:

The timezone offset was UTC +8:05:43 hours all of the period.

虽然我没太看明白具体是什么意思,但是我看到了“5 分 43 秒”:

我理解就是由于时区的变化,导致时间发生了重置。

接着我顺藤摸瓜,在 stackoverflow 上找到了这个:

https://stackoverflow.com/questions/6841333/why-is-subtracting-these-two-times-in-1927-giving-a-strange-result

当时我就震惊了。

这个 10 年前被提出的问题居然已经被浏览过 746k 次了,非常热门的问题了,我居然没注意到过:

这个问题具体是这样的:

你就大概瞟一眼,我给你翻译翻译。

提问者说,他发现 1927-12-31 23:54:07 到 1927-12-31 23:54:08 之间差了 353 秒,按理来说应该是 1 秒才对啊?

但是把时间改成下面这样,又正常了:

String str3 = "1927-12-31 23:54:07";  
String str4 = "1927-12-31 23:54:08";

我把程序粘出来你也可以跑一下,看看结果非常的神奇啊:

public static void main(String[] args) throws ParseException {
    SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");  
    String str3 = "1927-12-31 23:54:07";  
    String str4 = "1927-12-31 23:54:08";  
    Date sDt3 = sf.parse(str3);  
    Date sDt4 = sf.parse(str4);  
    long ld3 = sDt3.getTime() /1000;  
    long ld4 = sDt4.getTime() /1000;
    System.out.println(ld4-ld3);
}

但是当我跑了一遍之后,我发现:我去,说好的 353 秒呢?

跑出来怎么是 1 秒呢,毫无毛病啊:

我甚至怀疑是 jdk 版本的问题,于是我换了 jdk 9、11、15 都跑了一下,都是 1 秒!

这就很奇怪了啊。

感觉这个问题提的就有问题啊。

但是当我读了下面最高赞的答案之后,我才仿佛窥见了一点端倪。

这个回答比较长,我先全部截图下来给你看看:

比较长的原因是作者修改了几次回答。

为什么会修改回答呢?

且往后看吧,一切的答案都藏在这里面。

我选关键的给你说。

首先看第一段:

他说(1927年) 12 月 31 日的时候,上海的时区发生了变化。

而关于 1927 年上海的详细情况,他附上了一个超链接,这个超链接就是前面出现的网站,点进去之后是这样的:

但是这个里面显示:

No further time changes in 1927 in Shanghai

翻译过来就是:1927 年上海的时间没有进一步变化。

这特么就和他下面说的那一坨内容对不上了啊?

他下面说,在 1927 年底的午夜时分,时钟往回走了 5 分 52 秒。因此,"1927-12-31 23:54:08"实际上发生了两次,而 Java 取的是第二次的的时刻,因此存在差异。

看到这里其实我都懵逼了,这玩意前后不符啊,于是我又接着开始搜索。

直到我发现了这个:

https://coolshell.cn/articles/5075.html/comment-page-2#comments

这也是十年前的文章。

这里面作者把当时网站截了个图:

当年的截图显示:

在1927年12月31日23:59:59时,往后面的一秒应该是1928年1月1日 0:0:0,但是这个时间被往后调整了5分52秒,而成了,1927年12月31日的,23:54:08,于是,完成了352秒的穿越。

这说明了什么?

说明数据发生了篡改,有人篡改了网页上的信息!

到底是怎么回事呢?

我们回到 stackoverflow 接着往下看:

这是他第一次修改回答,因为 History changes...

历史发了变化了...

他这里说,如果用 TZDB 的 2013a 版本的数据,原来的问题将不再表现出完全相同的行为。

在 2013a 中,结果将是 358 秒,过渡时间为 23:54:03,而不是 23:54:08。

他提到了一个 TZDB,这是个啥东西呢?

我也不知道,但是我搜索了一下。

他应该说的是这个的东西。

https://www.iana.org/time-zones

看名字你也知道了,它是一个时区数据库,里面应该是维护的时区相关的数据。

也就是说,在这个时区数据库里面,用 2013a 版本的数据,前面的代码就是另外一种输出了。

也就是说数据确实发生了变化。

而关键的回答在于下一次编辑:

History has changed again...

历史再次发生了变化。

在这个时区数据库里面,2014f 版本中,变化的时间已经移到了1900-12-31,现在只是一个 343 秒的变化。

343 秒?

不就是我们前面的 5 分 43 秒吗?

好了,现在时差能对上了,343 秒,但是时间还是没对上啊。

我们的测试时间 1900-01-01 08:00:00,他这里写的时间是 1900-12-31。

差了整整一年呢?

好,我们看他最后一次编辑的内容:

我个人理解他要表达的意思是这样的。

Java 为了在时区上统一标准,所以来了个一刀切的政策。

统一的标准就是让 UTC 时区下 1900 年之前的任何瞬间都是标准时间。

至于产生的时差嘛...

就在最开始的时候补上去吧。

所以,1900-01-01 00:00:00 加上 8 小时时差,是 1900-01-01 08:00:00,在这个基础上预先加上 27 年后来自 1927-12-31 那个午夜由于时间回拨带来的 343 秒。

1900-01-01 08:05:43,我个人认为就是这样来的。

而前面 stackoverflow 里面对应的那个程序,我们现在执行是输出 1,但是在 10 年前,输出结果确实是 353 。

就像我把程序改成这样:

最终的输出结果不是 1,而是 -342。

时间,发生了“倒流”。

好了,又是一个没啥卵用的知识点。

最后,再补充两个冷知识。

第一个是我在 jdk bug 列表里面追溯了一下,能找到最早提出相关问题的时间是 2005 年:

https://bugs.openjdk.java.net/browse/JDK-6281408

在这个里面,官方是这样回复的:

这个问题不会被修复,以避免任何兼容性问题。

意思就是:问题我知道了,但是这玩意不太好弄,bug 先变成 feature 吧,就先这样吧。

别问,问就是有历史原因在里面。

第二个冷知识是,前面提到的,时区在 1927 年发生了变化。

你知道为什么吗?

我在某网站上找到了这样的描述:

https://zh.wikipedia.org/wiki/%E4%B8%AD%E5%9C%8B%E6%99%82%E5%8D%80

1928 年,就是民国 17 年。

那一年,中共军史上最早的红军,中国工农红军第四军成立。

那一年,毛主席在井冈山建立农村革命根据地。

那一年,星星之火,已成燎原之势。

荒腔走板

这期的荒腔走板,我就只放一张图:


至于这个政策最终会落实成什么样呢?

到时候大家有机会来成都软件园逛逛吧,看看灯火通明的高楼大厦,那是年轻人在燃烧的样子。

算了,再放一张图吧。

最后说一句

好了,看到了这里了,转发、在看、点赞随便安排一个吧,要是你都安排上我也不介意。写文章很累的,需要一点正反馈。给各位读者朋友们磕一个了!


与优秀的人在一起,自己也会优秀起来

高质量技术交流群,您还没加入吗?

赶紧点击加入我们,享受一起成长的快乐!


往期推荐

“VPN翻墙被大规模行政处罚” 是真的吗?

技术前沿:Redis推出性能碾压ES和Mongo的大杀器

墙裂推荐!卡神力作《代码随想录》,上架首日卖爆!

又双叒有兄弟因为 YYYY-MM-dd 被叫去加班了...

Java 18 都要来了,你不会还在用Java 8吧?


前沿技术早知道,弯道超车有希望
积累超车资本,从关注DD开始

点击阅读原文,送你免费Spring Boot教程!

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存